<template>
  <div id="chinaMap" ref="chinaMap">
    <div id="threejs"></div>
    <!-- 右侧按钮 -->
    <div class="rightButton">
      <div v-for="(item, index) in rightButItem" :key="index" :value="item.value" :class="item.selected ? 'selected common' : 'common'" @click="rightButClick">
        {{ item.name }}
      </div>
    </div>
    <!-- 地图名称元素 -->
    <div id="provinceName" style="display: none"></div>
    <!-- 棱柱上方数值元素 -->
    <div id="prismValue" style="display: none"></div>
    <!-- 地图标牌元素 -->
    <div id="mapTag" style="display: none">
      <div class="content">
        <div>旅客:</div>
        <div id="mapTag_value">1024万</div>
      </div>
      <div class="arrow"></div>
    </div>
    <!-- 弹框元素 -->
    <div id="popup" style="display: none">
      <div class="popup_line"></div>
      <div class="popup_Main">
        <div class="popupMain_top"></div>
        <div class="popup_content">
          <div class="popup_head">
            <div class="popup_title">
              <div class="title_icon"></div>
              <div id="popup_Name">湖北省</div>
            </div>
            <div class="close" @click="popupClose"></div>
          </div>
          <div class="popup_item">
            <div>当前流入：</div>
            <div class="item_value">388万人次</div>
          </div>
          <div class="popup_item">
            <div>景区容量：</div>
            <div class="item_value">2688万人次</div>
          </div>
          <div class="popup_item">
            <div>交通资源利用率：</div>
            <div class="item_value">88.7%</div>
          </div>
          <div class="popup_item">
            <div>省市热搜指数：</div>
            <div class="item_value">88.7%</div>
          </div>
        </div>
        <div class="popupMain_footer"></div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { onMounted, reactive, ref, watch } from "vue";
import * as THREE from "three";
// 引入TWEENJS
import TWEEN from "@tweenjs/tween.js";
import { CSS2DObject } from "three/addons/renderers/CSS2DRenderer.js";
// threejs基础配置，场景相机渲染器等
import { scene, camera, controls, renderer, css3DRenderer, css2DRenderer, outlinePass, composer, finalComposer, mirror, initScene } from "./baseConfig/index.js";
// 加载地图
import { loadChinaMapData, mapModel, cityData, mapUf, projection, waterObj } from "./initChinaMap/index.js";
// 地图底部网格背景
import { gridHelper, meshPoint } from "./backgroundMesh/index.js";
// 初始化鼠标移入地图浮动效果
import { initMapFloat } from "./mapFloat/index.js";
// 地图圆圈背景
import { circleUf, outerCircle, innerCircle, diffuseCircle, gradientPlane, planeUf } from "./backgroundCircle/index.js";
// 飞线组对象，更新飞线函数，飞线动画
import { flyGroup, updateFlyLine, flyLineAnimation } from "./flyLine/index.js";
// 棱柱组对象，创建棱柱函数
import { cylinderGlowArr, apertureAnimation, createCylinder } from "./prism/index.js";
import { apertureArr, provinceNameObj } from "./provinceName/index.js";
import { createMapTag, tagGroup } from "./mapTag/index.js";
import { particlesUpdate, createParticles, particles } from "./particles/index.js";
import { GUI } from "dat.gui";

// 右侧按钮选项
const rightButItem = reactive([
  { value: "tourism", name: "刷色图", selected: false },
  { value: "prism", name: "光柱", selected: false },
  { value: "flyLine", name: "飞线", selected: false },
  { value: "ripple", name: "波纹", selected: false },
  { value: "tag", name: "标牌", selected: true },
  { value: "particles", name: "粒子", selected: false },
  { value: "mirror", name: "倒影", selected: false },
]);
const gui = new GUI()
const chinaMap = ref();
// 发光模型
let glowModel = null;
// 时钟对象，用于获取两帧渲染之间的时间值
const clock = new THREE.Clock();
// 射线拾取中模型对象
let rayObj = null;
// 弹框元素
let divTag = null;
// css2D弹框对象
let css2Dpopup = null;
// 需要辉光的模型数组
let glowArr = [];
let elementWidth, elementHeight;
const cameraParams = {
  yawAngle: 0, // 偏航角
  highAngle: 60, // 俯视角
  height: 300, // 地图大小
  offsetX: 0, // X轴偏移
  offsetZ: 0, // Z轴偏移
  offsetY: 0, // Y轴偏移
  axeaOpen: false, // 坐标轴开启
  reset: () => {
    // 重置函数
    resetCameraParams();
  },
};

onMounted(async () => {
  const dom = chinaMap.value;
  elementWidth = dom.clientWidth;
  elementHeight = dom.clientHeight;
  initScene(elementWidth, elementHeight);
  document.getElementById("threejs").appendChild(renderer.domElement);
  document.getElementById("threejs").appendChild(css3DRenderer.domElement);
  document.getElementById("threejs").appendChild(css2DRenderer.domElement);
  // 创建棱柱
  // createPrismn();
  // 创建粒子
  createParticles();
  // 加载中国地图
  await loadChinaMapData();
  // 计算选中的省份模型大小，根据每个省份模型大小不同得出一个缩放值
  const box = new THREE.Box3().setFromObject(mapModel.clone());
  // 计算模型的长宽
  const length = box.max.x - box.min.x;
  const width = box.max.z - box.min.z;
  // 根据模型大小计算一个缩放值
  const scale = 12 - Math.sqrt(length * width) * 0.3;
  // 应用缩放值到省份模型上去
  mapModel.scale.set(scale, scale, 1);
  // 这时候计算省份模型，得出放大后的省份模型的中心点，并将其位置归于原点
  const scaledBox = new THREE.Box3().setFromObject(mapModel);
  const center = new THREE.Vector3();
  // 获取放大后模型的中心点
  scaledBox.getCenter(center);
  center.x += 5;
  // 将模型的位置调整，使缩放后的中心位于原点
  mapModel.position.sub(center);
  // 高度不增加
  mapModel.position.y += center.y;
  flyGroup.scale.set(scale, scale, scale);
  flyGroup.position.sub(center);
  flyGroup.position.y += center.y;
  // 创建地图标牌
  createMapTag(cityData, waterObj, center, scale);
  // 调整地级市名称大小
  for (let item in provinceNameObj) {
    provinceNameObj[item].scale.divideScalar(scale);
  }
  mapModel.traverse((item) => {
    if (item.name === "光柱") {
      item.scale.x = item.scale.x / scale;
      item.scale.z = item.scale.z / scale;
    }
  });
  // 初始化鼠标移入地图浮动效果
  initMapFloat(camera, mapModel, elementWidth, elementHeight);

  setGuiParams();
  // 初始化地图点击事件
  initMapClick();
  scene.add(mapModel, gridHelper, meshPoint, outerCircle, innerCircle, diffuseCircle, gradientPlane, tagGroup);
  // mapModel.position.x -= 5;
  // 设置需要辉光物体数组
  glowArr = [...cylinderGlowArr, flyGroup.children];
  // 开始循环渲染
  render();
  // 首次进入动画
  eventAnimation();
});

// 循环渲染
function render() {
  requestAnimationFrame(render);
  camera.updateProjectionMatrix();
  controls.update();
  // 两帧渲染间隔
  let deltaTime = clock.getDelta();
  // 地图模型侧边渐变效果
  mapUf.uTime.value += deltaTime;
  if (mapUf.uTime.value >= 5) {
    mapUf.uTime.value = 0.0;
  }
  // 地图光圈动态缩放动画
  apertureArr.map((mesh) => {
    // _s缩放值累加
    mesh._s += 0.05;
    // 小于20时，逐渐透明度逐渐增加到1
    if (mesh._s <= 20.0) {
      mesh.material.opacity = (mesh._s - 10.0) * 0.1;
    }
    // 20-30之间时，透明度逐渐递减到0
    else if (mesh._s > 20.0 && mesh._s <= 30) {
      mesh.material.opacity = 1 - (mesh._s - 20.0) / 10;
    }
    // 大于30进行重置
    else {
      mesh._s = 10.0;
    }
    // 设置新的缩放值
    mesh.scale.set(mesh._s, mesh._s, mesh._s);
  });
  // 背景外圈内圈旋转
  outerCircle.rotation.z -= 0.003;
  innerCircle.rotation.z += 0.003;
  // 飞线动画
  if (rightButItem[2].selected) {
    flyLineAnimation();
  }
  // 波纹扩散动画
  if (rightButItem[3].selected) {
    circleUf.uTime.value += deltaTime;
    if (circleUf.uTime.value >= 6) {
      circleUf.uTime.value = 0.0;
    }
  }
  // 粒子动画
  if (rightButItem[5].selected) {
    particlesUpdate();
  }
  // TWEEN更新
  TWEEN.update();
  // 将场景内的物体材质设置为黑色
  scene.traverse(darkenMaterial);
  // 渲染辉光
  composer.render();
  // 还原材质
  scene.traverse(restoreMaterial);
  // 最终渲染
  finalComposer.render();
  css3DRenderer.render(scene, camera);
  css2DRenderer.render(scene, camera);

  // renderer.render(scene, camera);
}

// 设置gui参数
function setGuiParams() {
  // 相机菜单
  const cameraFolder = gui.addFolder("相机");
  cameraFolder
    .add(cameraParams, "highAngle", 0, 90)
    .name("俯视角")
    .onChange((value) => {
      setCameraPosition();
    });

  cameraFolder
    .add(cameraParams, "yawAngle", -180, 180)
    .name("偏航角")
    .onChange((value) => {
      setCameraPosition();
    });

  cameraFolder
    .add(cameraParams, "height", 50, 500)
    .name("地图放大系数")
    .onChange((value) => {
      setCameraPosition();
    });

  cameraFolder
    .add(cameraParams, "offsetX", -300, 300)
    .name("X轴方向偏移")
    .onChange((value) => {
      setCameraPosition();
    });

  cameraFolder
    .add(cameraParams, "offsetZ", -300, 300)
    .name("Z轴方向偏移")
    .onChange((value) => {
      setCameraPosition();
    });

  cameraFolder
    .add(cameraParams, "offsetY", -150, 150)
    .name("Y轴方向偏移")
    .onChange((value) => {
      setCameraPosition();
    });


  cameraFolder.add(cameraParams, "reset").name("重置");
}

    // 重置相机配置参数，函数内配置项的值应与cameraParams属性初始时保持一致
function resetCameraParams() {
      cameraParams.height = 300;
      cameraParams.highAngle = 60;
      cameraParams.yawAngle = 0;
      cameraParams.offsetX = 0;
      cameraParams.offsetY = 0;
      cameraParams.offsetZ = 0;
      setCameraPosition();
    }

 function setCameraPosition() {
      const yawAngle = THREE.MathUtils.degToRad(cameraParams.yawAngle);
      const highAngle = THREE.MathUtils.degToRad(cameraParams.highAngle);
      camera.position.x = cameraParams.height * Math.cos(highAngle) * Math.sin(yawAngle);
      camera.position.y = cameraParams.height * Math.sin(highAngle);
      camera.position.z = cameraParams.height * Math.cos(highAngle) * Math.cos(yawAngle);
      camera.position.x += cameraParams.offsetX;
      camera.position.y += cameraParams.offsetY;
      camera.position.z += cameraParams.offsetZ;
      controls.target.set(cameraParams.offsetX, cameraParams.offsetY, cameraParams.offsetZ);
      controls.update();
    }

// 右侧按钮点击事件
function rightButClick(e) {
  const value = e.target.getAttribute("value");
  const clickItem = rightButItem.filter((obj) => obj.value === value)[0];
  clickItem.selected = !clickItem.selected;
  // 点击刷色图按钮
  if (clickItem.value === "tourism") {
    mapModel.traverse((item) => {
      if (item.color) {
        if (clickItem.selected) {
          item.material[0].color = item.color;
          item.material[0].metalness = 0.65;
          item.material[0].map = undefined;
          item.material[0].needsUpdate = true;
        } else {
          item.material[0].color = new THREE.Color("#00FFFF");
          item.material[0].metalness = 0.0;
          item.material[0].map = item.texture;
          item.material[0].needsUpdate = true;
        }
      }
    });
  }
  // 点击飞线按钮
  else if (clickItem.value === "flyLine") {
    if (clickItem.selected) {
      scene.add(flyGroup);
      updateFlyLine("郑州市", cityData);
    } else {
      scene.remove(flyGroup);
    }
  }
  // 点击棱柱按钮
  else if (clickItem.value === "prism") {
    mapModel.traverse((item) => {
      if (item.name === "光柱") {
        item.traverse((item) => (item.visible = clickItem.selected));
      }
    });
  }
  // 点击波纹按钮
  else if (clickItem.value === "ripple") {
    if (clickItem.selected) {
      diffuseCircle.visible = true;
    } else {
      diffuseCircle.visible = false;
    }
    circleUf.uTime.value = 0.0;
  }
  // 点击倒影按钮
  else if (clickItem.value === "mirror") {
    if (clickItem.selected) {
      scene.add(mirror);
      planeUf.opacitys.radius = 0.3;
      planeUf.opacitys.value = 0.5;
    } else {
      scene.remove(mirror);
      planeUf.opacitys.radius = 0.4;
      planeUf.opacitys.value = 0.7;
    }
  }
  // 点击标牌按钮
  else if (clickItem.value === "tag") {
    if (clickItem.selected) {
      scene.add(tagGroup);
    } else {
      for (const iterator of tagGroup.children) {
        css2DRenderer.domElement.removeChild(iterator.element); // 重点
      }
      scene.remove(tagGroup);
    }
  }
  // 点击粒子按钮
  else if (clickItem.value === "particles") {
    if (clickItem.selected) {
      scene.add(particles);
    } else {
      scene.remove(particles);
    }
  }
}
// 初始化地图点击事件
function initMapClick() {
  divTag = document.getElementById("popup");
  const widthScale = elementWidth / 1920;
  const heightScale = elementHeight / 941;
  divTag.style.top += (37 * heightScale).toFixed(2) + "px";
  divTag.style.left += (390 * widthScale).toFixed(2) + "px";
  // 转换为CSS2D对象
  css2Dpopup = new CSS2DObject(divTag);
  // 设置一个较高的渲染顺序，防止弹框被标牌等物体遮挡住
  css2Dpopup.renderOrder = 99;
  // 弹框名称元素
  const nameDiv = document.getElementById("popup_Name");
  // 鼠标点击事件
  addEventListener("click", (e) => {
    const px = e.offsetX;
    const py = e.offsetY;
    // 屏幕坐标转为标准设备坐标
    const x = (px / elementWidth) * 2 - 1;
    const y = -(py / elementHeight) * 2 + 1;
    // 创建射线
    const raycaster = new THREE.Raycaster();
    // 设置射线参数
    raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
    // 射线交叉计算拾取模型
    let intersects = raycaster.intersectObjects(mapModel.children);
    // 检测结果过滤边线
    intersects = intersects.filter(function (intersect) {
      return intersect.object.name !== "边线";
    });

    // 点击选中模型时
    if (intersects.length > 0) {
      // 清除上一次添加的发光模型
      if (rayObj) {
        rayObj.remove(glowModel);
      }
      const parent = intersects[0].object.parent;
      if (parent) {
        // 射线第一个拾取中的模型
        rayObj = intersects[0].object.parent;
        if (rayObj.name) {
          // 飞线模式下更新飞线，不显示弹框
          if (rightButItem[2].selected) {
            updateFlyLine(rayObj.name, cityData);
          } else {
            // 设置弹框位置，弹框名称等
            const center = rayObj.userData.center;
            css2Dpopup.position.set(center[0], -center[1], 0);
            rayObj.add(css2Dpopup);
            nameDiv.innerHTML = rayObj.name;

            // 给弹框加载创建渐变动画
            new TWEEN.Tween({ opacity: 0 })
              .to({ opacity: 1.0 }, 500)
              .onUpdate(function (obj) {
                //动态更新div元素透明度
                divTag.style.opacity = obj.opacity;
              })
              .start();
          }

          // 地图边线数据
          const mapLineData = rayObj.userData.cityData;
          console.log(mapLineData, "mapLineData");
          // 创建shape对象
          const shape = new THREE.Shape();
          // 当数据为多个多边形时
          if (mapLineData.type === "MultiPolygon") {
            // 遍历数据，绘制shape对象数据
            mapLineData.coordinates.forEach((coordinate, index) => {
              if (index === 0) {
                coordinate.forEach((rows) => {
                  rows.forEach((row) => {
                    const [x, y] = projection(row);
                    if (index === 0) {
                      shape.moveTo(x, y);
                    }
                    shape.lineTo(x, y);
                  });
                });
              }
            });
          }
          // 当数据为单个多边形时
          if (mapLineData.type === "Polygon") {
            mapLineData.coordinates.forEach((coordinate) => {
              // 遍历数据，绘制shape对象数据
              mapLineData.coordinates.forEach((rows, index) => {
                if (index === 0) {
                  rows.forEach((row) => {
                    const [x, y] = projection(row);
                    if (index === 0) {
                      shape.moveTo(x, y);
                    }
                    shape.lineTo(x, y);
                  });
                }
              });
            });
          }
          // 创建形状几何体，shape对象作为参数
          const geometry = new THREE.ShapeGeometry(shape);
          const material = new THREE.MeshBasicMaterial({
            color: rayObj.children[0].material[0].color,
            map: rayObj.children[0].material[0].map,
            side: THREE.DoubleSide,
          });
          let mesh = new THREE.Mesh(geometry, material);
          mesh.rotateX(-Math.PI);
          mesh.name = "发光模型";
          glowModel = mesh;
          rayObj.add(glowModel);
          // 设置发光模型进行发光
          outlinePass.selectedObjects = [glowModel];
        }
      }
    }
    // 未选中模型
    else {
      if (glowModel) {
        outlinePass.selectedObjects = [];
        rayObj.remove(glowModel);
        // 给弹框清除创建渐变动画
        new TWEEN.Tween({ opacity: 1 })
          .to({ opacity: 0 }, 500)
          .onUpdate(function (obj) {
            //动态更新div元素透明度
            divTag.style.opacity = obj.opacity;
          })
          .onComplete(function () {
            // 清除弹框
            rayObj.remove(css2Dpopup);
          })
          .start();
      }
    }
  });
}
// 弹框关闭事件
function popupClose() {
  if (glowModel) {
    outlinePass.selectedObjects = [];
    rayObj.remove(glowModel);
    // 给弹框清除创建渐变动画
    new TWEEN.Tween({ opacity: 1 })
      .to({ opacity: 0 }, 500)
      .onUpdate(function (obj) {
        //动态更新div元素透明度
        divTag.style.opacity = obj.opacity;
      })
      .onComplete(function () {
        // 清除弹框
        rayObj.remove(css2Dpopup);
      })
      .start();
  }
}
// 将材质设置成黑色
function darkenMaterial(obj) {
  // 场景颜色单独保存
  if (obj instanceof THREE.Scene) {
    obj.bg = obj.background;
    obj.background = null;
  }
  const material = obj.material;
  if (material && !glowArr.includes(obj)) {
    obj.originalMaterial = obj.material;
    const Proto = Object.getPrototypeOf(material).constructor;
    obj.material = new Proto({ color: new THREE.Color("#000") });
  }
}
// 还原材质
function restoreMaterial(obj) {
  if (obj instanceof THREE.Scene) {
    // obj.background = obj.bg;
  }
  if (!obj.originalMaterial) return;
  obj.material = obj.originalMaterial;
  delete obj.originalMaterial;
}
// 首次进入动画
function eventAnimation() {
  new TWEEN.Tween(camera.clone().position)
    .to(new THREE.Vector3(-5, 250, 150), 1500)
    .easing(TWEEN.Easing.Sinusoidal.InOut)
    .onUpdate((e) => {
      camera.position.copy(e);
      controls.target.set(-5, 0, 10);
      controls.update();
    })
    .start();
}
</script>
<style lang="less">
body,
html {
  font-size: 0.8vw;
}
#chinaMap {
  width: 100vw;
  height: 100vh;
  position: absolute;
  overflow: hidden;
}
#threejs {
  width: 100%;
  height: 100%;
}
.rightButton {
  position: absolute;
  right: 1vw;
  bottom: 40vh;
  width: 4vw;

  .common {
    width: 100%;
    height: 3vh;
    border: 1px solid #00ffff;
    display: flex;
    justify-content: center;
    align-items: center;
    margin: 1.2vh 0;
    color: #fafafa;
    opacity: 0.5;
    font-size: 0.8vw;
    cursor: pointer;
    transition: 1s;
  }

  .selected {
    opacity: 1 !important;
    transition: 1s;
  }
}
#provinceName {
  pointer-events: none;
  position: absolute;
  left: 0;
  top: 0;
  color: #8ee5ee;
  padding: 10px;
  width: 200px;
  height: 20px;
  line-height: 20px;
  text-align: center;
  font-size: 13px;
}
#popup {
  z-index: 999;
  position: absolute;
  left: 0px;
  top: 0px;
  width: 41.66vw;
  height: 26.59vh;
  display: flex;

  .popup_line {
    margin-top: 4%;
    width: 24%;
    height: 26%;
    background: url("../../public/popup_line.png") no-repeat;
    background-size: 100% 100%;
  }
  .popup_Main {
    width: 35%;
    height: 80%;

    .popupMain_top {
      width: 100%;
      height: 10%;
      background: url("../../public/popupMain_head.png") no-repeat;
      background-size: 100% 100%;
    }
    .popupMain_footer {
      width: 100%;
      height: 10%;
      background: url("../../public/popupMain_footer.png") no-repeat;
      background-size: 100% 100%;
    }
    .popup_content {
      color: #fafafa;
      // background: rgba(47, 53, 121, 0.9);
      background-image: linear-gradient(to bottom, rgba(15, 36, 77, 1), rgba(8, 124, 190, 1));
      border-radius: 10px;
      width: 100%;
      height: 70%;
      padding: 5% 0%;
      .popup_head {
        width: 100%;
        height: 12%;
        margin-bottom: 2%;
        display: flex;
        align-items: center;
        .popup_title {
          color: #8ee5ee;
          font-size: 1vw;
          letter-spacing: 5px;
          width: 88%;
          height: 100%;
          display: flex;
          align-items: center;

          .title_icon {
            width: 0.33vw;
            height: 100%;
            background: #2586ff;
            margin-right: 10%;
          }
        }
        .close {
          cursor: pointer;
          pointer-events: auto;
          width: 1.5vw;
          height: 1.5vw;
          background: url("../../public/close.png") no-repeat;
          background-size: 100% 100%;
        }
      }
      .popup_item {
        display: flex;
        align-items: center;
        width: 85%;
        padding-left: 5%;
        height: 18%;
        // background: rgb(160, 196, 221);
        border-radius: 10px;
        margin: 2.5% 0%;
        margin-left: 10%;

        div {
          line-height: 100%;
          margin-right: 10%;
        }
        .item_value {
          font-size: 0.9vw;
          color: #00ffff;
          font-weight: 600;
          letter-spacing: 2px;
        }
      }
    }
  }
}
#prismValue {
  position: absolute;
  top: 0;
  left: 0;
  color: #bbffff;
}
#mapTag {
  z-index: 997;
  position: absolute;
  top: 0;
  left: 0;
  font-size: 0.6vw;
  width: 5vw;
  height: 4.7vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;

  .content {
    width: 100%;
    height: calc(100% - 1vw);
    // background: #0e1937;
    background: #0e2346;
    border: 1px solid #6298a9;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fafafa;

    #mapTag_value {
      color: #ffd700;
    }
  }
  .content::before {
    content: "";
    width: 100%;
    height: calc(100% - 1vw);
    position: absolute;
    background: linear-gradient(to top, #26aad1, #26aad1) left top no-repeat,
      //上左
      linear-gradient(to right, #26aad1, #26aad1) left top no-repeat,
      linear-gradient(to top, #26aad1, #26aad1) right bottom no-repeat,
      //下右
      linear-gradient(to left, #26aad1, #26aad1) right bottom no-repeat; //右下
    background-size: 2px 10px, 16px 2px, 2px 10px, 16px 2px;
    pointer-events: none;
  }

  .arrow {
    background: url("../../public/arrow.png") no-repeat;
    background-size: 100% 100%;
    width: 1vw;
    height: 1vw;
  }
}
</style>
